package com.pmstation.skin

import math.random
import javax.imageio.ImageIO
import java.awt.image.BufferedImage
import java.io.{InputStream, File}
import collection.mutable
import java.awt._
import java.net.URL

/**
 */

object Skin{
  object skinPixelsSrc extends FilePixelsSrc("/skin/",
      Nil ++ Array("skin-parts.png","parts2.png","parts3.png")
      ++ (1 to 32).map("shaper"+_+".png")
    )

  def doLearn(maxBrFilter: MaxBrightSkinFilter): Unit = {
    val dirLearn = new File("/tmp/nudes/learn")
    var totalLearned = 0
    dirLearn.listFiles.foreach {
      f =>
        val l = maxBrFilter.learnProcess(ImageIO.read(f))
        totalLearned += l
        println("learn %s res %s total %s ".format(f, l, totalLearned))
    }
  }

  def mkFilter = {
    val maxBrFilter = new MaxBrightSkinFilter(skinPixelsSrc, nonSkinPixelsSrc)

    //doLearn(maxBrFilter)

    maxBrFilter
  }

  def main(args:Array[String]){

    //println("easyNotSkin volume %s".format( filterVolume(easyNotSkin) ))

    //val imgRes = this.getClass.getResource("/blue-ridge-flower.jpg")
    //val imgRes = this.getClass.getResource("/test1.jpg")
    //val imgRes = new URL("file:/opt/project/learnimg/skin1.jpg")
    val imgRes = this.getClass.getResource("/skin/unittest-parts.png")
    println("Skin..."+imgRes)

    val fout = new File("/tmp/skin-res.jpeg")

    val filter = new SkinFilter
    val maxBrFilter = mkFilter


    for( b <- 100 to 105 ){
    //val singleTestColor = new Color(228,159,102)
      val singleTestColor = new Color(228,159,b)
      println("singleTestColor %s isSkin=%s  %s".
              format(singleTestColor, maxBrFilter.isSkin(singleTestColor.getRGB),
                maxBrFilter.testSkin(singleTestColor.getRGB) ))
    }

    //val nud = new NudityDetection( bufImg("/test1.jpg"), maxBrFilter.isSkin )
    val nud = new NudityDetection( ImageIO.read(new File("/tmp/nudes/3.jpg")), maxBrFilter.isSkin )

    val nudRegs = new nud.Regions
    nudRegs.showRegions("/tmp/regions.jpg")

        val nudPoly = new nudRegs.BoundingPolygon
    nudPoly.showPolygon("/tmp/polygon.jpg")

    val nudRes = nud.check()
    println("nudRes %s".format(nudRes))

    unitTestNoSkin(maxBrFilter.isSkin)
    unitTestSkin(maxBrFilter.isSkin)

    //val skinRes = filterImg(imgRes.openStream, easyNotSkin)
    //val skinRes = filterImg(imgRes.openStream, /*filter.isSkin*/ x => !easyNotSkin(x) )
    //val skinRes = filterImg(imgRes.openStream, filter.isSkin )
    val skinRes = filterImg(imgRes.openStream, maxBrFilter.isSkin)

    ImageIO.write(skinRes, "jpeg", fout)
    //println("Done %s  volume %s".format(fout, filterImgVolume(imgRes.openStream, easyNotSkin) ))
    println("Done %s  volume %s".format(fout, filterImgVolume(imgRes.openStream, filter.isSkin) ))
    //println("Done %s  volume %s".format(fout, filterImgVolume(imgRes.openStream, maxBrFilter.isSkin) ))
    //skinPixelsSrcAnalyze()
    skinPixelsSrcAnalyze2D()

    val foutm = new File("/tmp/skin-maxbr.png")
    ImageIO.write(maxBrFilter.paintHisto(new Color(228,159,102)), "png", foutm)
    println("%s ready".format(foutm))
  }

  def bufImg(resName:String):BufferedImage = {
    val imgRes = this.getClass.getResource(resName)
    ImageIO.read(imgRes.openStream)
  }

  def unitTestNoSkin(isSkin:Int=>Boolean){
    var count, countErr = 0
    val src = new FilePixelsSrc("/nonskin/", Array("unittest-forest-path.jpg"))
    for( rgb <- src ){
      count += 1
      if( isSkin(rgb) ){
        countErr += 1
      }
    }
    println( "%s total=%s  err=%s".format(src, count, countErr))
  }
  def unitTestSkin(isSkin:Int=>Boolean){
    var count, countErr = 0
    val src = new FilePixelsSrc("/skin/", Array("unittest-parts.png"))
    for( rgb <- src ){
      count += 1
      if( !isSkin(rgb) ){
        countErr += 1
      }
    }
    println( "%s total=%s  err=%s".format(src, count, countErr))
  }

  class ImgPixelIterator(img:BufferedImage){
    def foreach(f:Int => Unit){
      for( i<-0 until img.getWidth; j<-0 until img.getHeight ){
        f( img.getRGB(i,j) )
      }
    }
  }

  class FilePixelsSrc(root:String, files: Seq[String]) extends PixelsSrc{
    def foreach(f:Int => Unit){
      for(file <- files ){

        val img =  ImageIO.read( this.getClass.getResource(root+file) )
        val white = Color.WHITE.getRGB & 0xffffff
        for(i<-0 until img.getWidth; j<-0 until img.getHeight){
          val cij = img.getRGB(i,j) & 0xffffff
          if(cij!=white){
            f(cij)
          }
        }

      }
    }

    override def toString = root + " ==> " + files
  }


  object nonSkinPixelsSrc extends FilePixelsSrc("/nonskin/",
      Array("blue-ridge-flower.jpg","sand-dune.jpg","footprints-sand.jpg",
        "sunset_sand_dunes_grass.jpg","forest1.jpg"))

  class ColorHisto2D{
    val a = Array.ofDim[Int](256,256)
    var maxVal=0
    def inc(i:Int, j:Int){
      a(i)(j) += 1
      maxVal = maxVal max a(i)(j)
    }
    def resultColor(i:Int, j:Int) = a(i)(j)*255/maxVal*256
    def resultBin(i:Int, j:Int) = if (resultColor(i,j)>0) Color.WHITE.getRGB else 0
  }

  class SkinFilter{
    val bsize=8
    val bcount=256/bsize
    val blocks = Array.ofDim[Int](bcount, bcount, bcount)

    def isSkin(rgb: Int):Boolean={
      val R = ((rgb >> 16) & 0xFF) / bsize
      val G = ((rgb >> 8) & 0xFF) / bsize
      val B = ((rgb >> 0) & 0xFF) / bsize

      blocks(R)(G)(B)>0
    }

    for(rgb <- skinPixelsSrc){
      val c = new Color(rgb)
      blocks(c.getRed / bsize)(c.getGreen / bsize)(c.getBlue / bsize) += 1
    }
    var countAll, countSkin=0
    for( r<-0 until bcount; g<-0 until bcount; b<-0 until bcount ){
      countAll += 1
      if( blocks(r)(g)(b)>0 ){
        countSkin += 1
      }
    }
    println("SkinFilter blocks  countAll=%s  countSkin=%s".format(countAll, countSkin))
  }


  def skinPixelsSrcAnalyze2D(){
    var count=0
    val rg, gb, rb = new ColorHisto2D
    var maxVal=0
    for(rgb <- skinPixelsSrc){
      count += 1
      val c = new Color(rgb)
      rg.inc( c.getRed, c.getGreen )
      gb.inc( c.getGreen, c.getBlue )
      rb.inc( c.getRed, c.getBlue )
    }
    val imgNew = new BufferedImage(256*3, 256*2, BufferedImage.TYPE_INT_RGB)

    for(i<-0 to 255; j<-0 to 255){
      imgNew.setRGB(i,j, rg.resultColor(i,j) )
      imgNew.setRGB(i+256,j, gb.resultColor(i,j) )
      imgNew.setRGB(i+256*2,j, rb.resultColor(i,j) )

      imgNew.setRGB(i,j+256, rg.resultBin(i,j) )
      imgNew.setRGB(i+256,j+256, gb.resultBin(i,j) )
      imgNew.setRGB(i+256*2,j+256, rb.resultBin(i,j) )
    }
    val g = imgNew.getGraphics
    g.setColor(Color.WHITE)
    g.drawLine(0,256,256*3,256)
    g.drawLine(256,0,256,256*2)
    g.drawLine(256*2,0, 256*2,256*2)

    val fout = new File("/tmp/skin-res-2d-rg.png")
    ImageIO.write(imgNew, "png", fout)
    println("Done %s  count=%s".format(fout,count))

  }


  def skinPixelsSrcAnalyze(){
    var count=0
    val set = new mutable.HashSet[Int]
    val reds = new Array[Int](256)
    val greens = new Array[Int](256)
    val blues = new Array[Int](256)
    for(rgb <- skinPixelsSrc){
      count += 1
      set.add(rgb)
      reds( new Color(rgb).getRed ) += 1
      greens( new Color(rgb).getGreen ) += 1
      blues( new Color(rgb).getBlue ) += 1
    }
    println("skinsrc count=%s setSize=%s".format(count, set.size))
    for(i<-0 to 255){
      println("histo r/g/b #%4d  %4d %4d %4d ".format(i, reds(i), greens(i), blues(i)))
    }
  }

  def filterImg(imgStream:InputStream, filter:Int=>Boolean):BufferedImage = {
    val img : BufferedImage = ImageIO.read(imgStream)
    val imgNew = new BufferedImage(img.getWidth, img.getHeight, img.getType)
    for(i<-0 until img.getWidth; j<-0 until img.getHeight){
      val cij = img.getRGB(i,j)
      val color = if( filter(cij) ) Color.WHITE.getRGB else cij
      imgNew.setRGB(i,j,color)
    }
    imgNew
  }

  def filterImgVolume(imgStream:InputStream, filter:Int=>Boolean):Double = {
    var count,hit=0
    val img : BufferedImage = ImageIO.read(imgStream)
    for(i<-0 until img.getWidth; j<-0 until img.getHeight){
      val cij = img.getRGB(i,j)
      count += 1
      if( filter(cij) ) hit += 1
    }
    hit.toDouble / count
  }

  def filterVolume( filter:Int=>Boolean ) = {
    val count=10*1000*1000
    var hit=0
    for(i<-1 to count){
      if( filter(rndColor) ){
        hit+=1
      }
    }
    hit.toDouble / count
  }

  def rndColor:Int = new Color( (random*256).toInt, (random*256).toInt, (random*256).toInt ).getRGB


}